package com.mccken.rapid.client.core;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.ProviderConfig;
import org.apache.dubbo.config.spring.ServiceBean;

import com.mccken.rapid.client.RapidInvoker;
import com.mccken.rapid.client.RapidProtocol;
import com.mccken.rapid.client.RapidService;
import com.mccken.rapid.client.core.support.dubbo.DubboConstants;
import com.mccken.rapid.common.config.DubboServiceInvoker;
import com.mccken.rapid.common.config.HttpServiceInvoker;
import com.mccken.rapid.common.config.ServiceDefinition;
import com.mccken.rapid.common.config.ServiceInvoker;
import com.mccken.rapid.common.constants.BasicConst;

/**
 * 注解扫描类，用于扫描所有的用户定义的 @RapidService 和 @RapidInvoker
 *
 * @program: rapid_gateway
 * @description:
 * @author: mccken
 * @create: 2022-03-03 09:52
 **/
public class RapidAnnotationScanner {
	private RapidAnnotationScanner() {
	}

	private static class SingletonHolder {
		static final RapidAnnotationScanner INSTANCE = new RapidAnnotationScanner();
	}

	public static RapidAnnotationScanner getInstance() {
		return SingletonHolder.INSTANCE;
	}

	/**
	 * 扫描传入的Bean对象，最终返回一个ServiceDefinition
	 */
	public synchronized ServiceDefinition scanbuilder(Object bean, Object... args) {
		Class<?> clazz = bean.getClass();
		boolean isPresent = clazz.isAnnotationPresent(RapidService.class);

		if (isPresent) {
			RapidService rapidService = clazz.getAnnotation(RapidService.class);
			String serviceId = rapidService.serviceId();
			RapidProtocol protocol = rapidService.protocol();
			String patternPath = rapidService.patternPath();
			String version = rapidService.version();

			ServiceDefinition serviceDefinition = new ServiceDefinition();
			Map<String, ServiceInvoker> invokerMap = new HashMap<>();

			Method[] methods = clazz.getMethods();
			if (methods != null && methods.length > 0) {
				for (Method method : methods) {
					RapidInvoker rapidInvoker = method.getAnnotation(RapidInvoker.class);
					if (rapidInvoker == null) {
						continue;
					}

					String path = rapidInvoker.path();
					
					switch (protocol) {
						case HTTP:
							HttpServiceInvoker httpServiceInvoker = createHttpServiceInvoker(path, bean, method);
							invokerMap.put(path, httpServiceInvoker);
							break;
						case DUBBO:
							ServiceBean serviceBean = (ServiceBean) args[0];
							DubboServiceInvoker dubboServiceInvoker = createDubboServiceInvoker(path, serviceBean, method);
							// dubbo version reset for serviceDefinition version
							String dubboServiceInvokerVersion = dubboServiceInvoker.getVersion();
							if (!StringUtils.isBlank(dubboServiceInvokerVersion)) {
								version = dubboServiceInvokerVersion;	
							}
							invokerMap.put(path, dubboServiceInvoker);
						default:
							break;
					}
				}
			}
		
			// 设置属性
			serviceDefinition.setUniqueId(serviceId + BasicConst.COLON_SEPARATOR + version);
			serviceDefinition.setServiceId(serviceId);
			serviceDefinition.setVersion(version);
			serviceDefinition.setProtocol(protocol.getCode());
			serviceDefinition.setPatternPath(patternPath);
			serviceDefinition.setEnable(true);
			serviceDefinition.setInvokerMap(invokerMap);
			return serviceDefinition;
		}
		return null;
	}

	/**
	 * 构建HttpServiceInvoker对象
	 * 
	 * @param path
	 * @param bean
	 * @param method
	 * @return
	 */
	private HttpServiceInvoker createHttpServiceInvoker(String path, Object bean, Method method) {
		HttpServiceInvoker httpServiceInvoker = new HttpServiceInvoker();
		httpServiceInvoker.setInvokerPath(path);
		return httpServiceInvoker;
	}
	
	
	private DubboServiceInvoker createDubboServiceInvoker(String path, ServiceBean<?> serviceBean, Method method) {
		DubboServiceInvoker dubboServiceInvoker = new DubboServiceInvoker();
		dubboServiceInvoker.setInvokerPath(path);

		String methodName = method.getName();
		String registerAddress = serviceBean.getRegistry().getAddress();
		String interfaceClass = serviceBean.getInterface();
		
		dubboServiceInvoker.setRegisterAddress(registerAddress);
		dubboServiceInvoker.setMethodName(methodName);
		dubboServiceInvoker.setInterfaceClass(interfaceClass);

		String[] parameterTypes = new String[method.getParameterCount()];
		Class<?>[] classes = method.getParameterTypes();
		for (int i = 0; i < classes.length; i++) {
			parameterTypes[i] = classes[i].getName();
		}
		
		dubboServiceInvoker.setParameterTypes(parameterTypes);

		Integer serviceTime = serviceBean.getTimeout();
		if (serviceTime == null || serviceTime == 0) {
			ProviderConfig providerConfig = serviceBean.getProvider();
			if (providerConfig != null) {
				Integer providerConfigTimeout = providerConfig.getTimeout();
				if (providerConfigTimeout == null || providerConfigTimeout == 0) {
					serviceTime = DubboConstants.DUBBO_TIMEOUT;	
				} else {
					serviceTime = providerConfigTimeout;
				}
			}
		}

		String version = serviceBean.getVersion();
		dubboServiceInvoker.setVersion(version);
		return dubboServiceInvoker;
	}
	
	
	
}
